package online.shuita.gitee.mojo.doclet;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.RootDoc;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 解析类文件中的中文注释
 */
@Slf4j
public class Doclet {
    private static RootDoc rootDoc;
    private ClassLoader loader;
    private String javaBeanFilePath;
    private List<String> jarList;

    public static boolean start(RootDoc root) {
        rootDoc = root;
        return true;
    }

    public Doclet(List<String> jarList, ClassLoader loader) {
        this.jarList = jarList;
        this.loader = loader;
    }

    public Doclet(String javaBeanFilePath, List<String> jarList, ClassLoader loader) {
        this.javaBeanFilePath = javaBeanFilePath;
        this.jarList = jarList;
        this.loader = loader;
    }

    public ClassDoc exec() {
        return exec(javaBeanFilePath);
    }

    public ClassDoc exec(String sourceFile) {
        log.info("start exec doclet。sourceFile={}", sourceFile);
        ClassDoc modelClassDoc = new ClassDoc();
        if (null != jarList && jarList.size() > 0) {
            log.info(loader.getResource(".").toString());
            com.sun.tools.javadoc.Main.execute(new String[]{
                    "-classpath",
                    String.join(";", jarList),
                    "-doclet", Doclet.class.getName(), "-docletpath",
                    loader.getResource(".").getPath(), "-encoding", "utf-8", sourceFile});
        } else {
            log.info(Doclet.class.getResource(".").toString());
            com.sun.tools.javadoc.Main.execute(new String[]{
                    "-doclet", Doclet.class.getName(), "-docletpath",
                    Doclet.class.getResource("/").getPath(), "-encoding", "utf-8", sourceFile});
        }

        com.sun.javadoc.ClassDoc[] classes = rootDoc.classes();

        if (classes == null || classes.length == 0) {
            return modelClassDoc;
        }

        List<FieldEntry> entrys = Lists.newArrayList();
        com.sun.javadoc.ClassDoc classDoc = classes[0];
        // 获取类的名称
        modelClassDoc.setModelClassName(classDoc.name());
        // 获取类的注释
        String classComment = classDoc.getRawCommentText();
        String spitStr = "\n";
        for (String msg : classComment.split(spitStr)) {
            if (!msg.trim().startsWith("@") && msg.trim().length() > 0) {
                modelClassDoc.setModelCommentText(msg);
                break;
            }
        }

        // 获取属性名称和注释
        FieldDoc[] fields = classDoc.fields(false);
        for (FieldDoc field : fields) {
            entrys.add(new FieldEntry(field.name(), field.type().typeName(), field.commentText()));
        }

        modelClassDoc.setFieldEntryList(entrys);
        modelClassDoc.setFieldEntryMap(entrys.stream().collect(Collectors.toMap(FieldEntry::getfName, item -> item)));

        MethodDoc[] methodDocs = classDoc.methods(false);
        modelClassDoc.setMethodEntryMap(Arrays.stream(methodDocs).map(m -> {
            // 获取方法的注释
            String mComment = m.getRawCommentText();

            Map<String, String> pdescMap = Maps.newHashMap();
            String curPara = null;
            StringBuffer curParaDesc = new StringBuffer();
            for (String msg : mComment.split("\n")) {
                msg = msg.trim();
                if (msg.startsWith("@param")) {
                    //解析入参备注
                    if (null != curPara) {
                        pdescMap.putIfAbsent(curPara, curParaDesc.toString());
                        curPara = null;
                        curParaDesc.setLength(0);
                    }
                    String s = msg.replaceFirst("@param", "").trim();
                    if ("".equalsIgnoreCase(s)) {
                        continue;
                    }
                    if(s.contains(" ")){
                        curPara = s.substring(0, s.indexOf(" "));
                        curParaDesc.append(s.replace(curPara, "").trim()).append("\n");
                    }else{
                        curPara = s;
                    }
                    break;
                } else if (msg.startsWith("@") && null != curPara) {
                    pdescMap.putIfAbsent(curPara, curParaDesc.toString());
                    curPara = null;
                    curParaDesc.setLength(0);
                } else if (null != curPara) {
                    curParaDesc.append(msg.trim()).append("\n");
                }
            }
            if (null != curPara) {
                pdescMap.putIfAbsent(curPara, curParaDesc.toString());
            }

            Parameter[] parameters = m.parameters();
            return MethodEntry.builder().mName(m.toString()).mExplain(m.commentText())
                    .parameterList(Arrays.stream(parameters).map(p -> {
                        return ParameterEntry.builder().parameterName(p.name())
                                .type(p.typeName())
                                .parameterDesc(pdescMap.get(p.name()))
                                .build();
                    }).collect(Collectors.toList()))
                    .build();
        }).collect(Collectors.toMap(MethodEntry::getMName, item -> item)));

        log.info("end exec doclet");
        return modelClassDoc;
    }

    // 测试一下
    public static void main(String[] args) {
        String jarStr = "D:\\workspace\\RegionalOperation\\scheduler-sac-app\\scheduler-sac-api\\target\\classes,D:\\maven_resposity\\com\\ymm\\trade-boot-data\\1.1.5\\trade-boot-data-1.1.5.jar,D:\\maven_resposity\\com\\google\\guava\\guava\\18.0\\guava-18.0.jar,D:\\maven_resposity\\com\\ymm\\trade-boot-error\\1.1.5\\trade-boot-error-1.1.5.jar,D:\\maven_resposity\\com\\ymm\\trade-boot-dependencies\\1.1.5\\trade-boot-dependencies-1.1.5.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-starter-web\\2.0.4.RELEASE\\spring-boot-starter-web-2.0.4.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-starter\\2.0.4.RELEASE\\spring-boot-starter-2.0.4.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-autoconfigure\\2.0.4.RELEASE\\spring-boot-autoconfigure-2.0.4.RELEASE.jar,D:\\maven_resposity\\javax\\annotation\\javax.annotation-api\\1.3.2\\javax.annotation-api-1.3.2.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-starter-json\\2.0.4.RELEASE\\spring-boot-starter-json-2.0.4.RELEASE.jar,D:\\maven_resposity\\com\\fasterxml\\jackson\\core\\jackson-databind\\2.9.6\\jackson-databind-2.9.6.jar,D:\\maven_resposity\\com\\fasterxml\\jackson\\core\\jackson-annotations\\2.9.0\\jackson-annotations-2.9.0.jar,D:\\maven_resposity\\com\\fasterxml\\jackson\\core\\jackson-core\\2.9.6\\jackson-core-2.9.6.jar,D:\\maven_resposity\\com\\fasterxml\\jackson\\datatype\\jackson-datatype-jdk8\\2.9.6\\jackson-datatype-jdk8-2.9.6.jar,D:\\maven_resposity\\com\\fasterxml\\jackson\\datatype\\jackson-datatype-jsr310\\2.9.6\\jackson-datatype-jsr310-2.9.6.jar,D:\\maven_resposity\\com\\fasterxml\\jackson\\module\\jackson-module-parameter-names\\2.9.6\\jackson-module-parameter-names-2.9.6.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-starter-tomcat\\2.0.4.RELEASE\\spring-boot-starter-tomcat-2.0.4.RELEASE.jar,D:\\maven_resposity\\org\\apache\\tomcat\\embed\\tomcat-embed-core\\8.5.32\\tomcat-embed-core-8.5.32.jar,D:\\maven_resposity\\org\\apache\\tomcat\\embed\\tomcat-embed-el\\8.5.32\\tomcat-embed-el-8.5.32.jar,D:\\maven_resposity\\org\\apache\\tomcat\\embed\\tomcat-embed-websocket\\8.5.32\\tomcat-embed-websocket-8.5.32.jar,D:\\maven_resposity\\org\\hibernate\\validator\\hibernate-validator\\6.0.11.Final\\hibernate-validator-6.0.11.Final.jar,D:\\maven_resposity\\javax\\validation\\validation-api\\2.0.1.Final\\validation-api-2.0.1.Final.jar,D:\\maven_resposity\\org\\jboss\\logging\\jboss-logging\\3.3.2.Final\\jboss-logging-3.3.2.Final.jar,D:\\maven_resposity\\com\\fasterxml\\classmate\\1.3.4\\classmate-1.3.4.jar,D:\\maven_resposity\\org\\springframework\\spring-web\\5.0.8.RELEASE\\spring-web-5.0.8.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\spring-webmvc\\5.0.8.RELEASE\\spring-webmvc-5.0.8.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-starter-actuator\\2.0.4.RELEASE\\spring-boot-starter-actuator-2.0.4.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-actuator-autoconfigure\\2.0.4.RELEASE\\spring-boot-actuator-autoconfigure-2.0.4.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-actuator\\2.0.4.RELEASE\\spring-boot-actuator-2.0.4.RELEASE.jar,D:\\maven_resposity\\io\\micrometer\\micrometer-core\\1.0.6\\micrometer-core-1.0.6.jar,D:\\maven_resposity\\org\\hdrhistogram\\HdrHistogram\\2.1.10\\HdrHistogram-2.1.10.jar,D:\\maven_resposity\\org\\latencyutils\\LatencyUtils\\2.0.3\\LatencyUtils-2.0.3.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-starter-aop\\2.0.4.RELEASE\\spring-boot-starter-aop-2.0.4.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot-starter-log4j2\\2.0.4.RELEASE\\spring-boot-starter-log4j2-2.0.4.RELEASE.jar,D:\\maven_resposity\\org\\apache\\logging\\log4j\\log4j-slf4j-impl\\2.10.0\\log4j-slf4j-impl-2.10.0.jar,D:\\maven_resposity\\org\\slf4j\\slf4j-api\\1.7.25\\slf4j-api-1.7.25.jar,D:\\maven_resposity\\org\\apache\\logging\\log4j\\log4j-api\\2.10.0\\log4j-api-2.10.0.jar,D:\\maven_resposity\\org\\apache\\logging\\log4j\\log4j-core\\2.10.0\\log4j-core-2.10.0.jar,D:\\maven_resposity\\org\\apache\\logging\\log4j\\log4j-jul\\2.10.0\\log4j-jul-2.10.0.jar,D:\\maven_resposity\\org\\slf4j\\jul-to-slf4j\\1.7.25\\jul-to-slf4j-1.7.25.jar,D:\\maven_resposity\\org\\aspectj\\aspectjweaver\\1.8.13\\aspectjweaver-1.8.13.jar,D:\\maven_resposity\\org\\apache\\commons\\commons-lang3\\3.7\\commons-lang3-3.7.jar,D:\\maven_resposity\\javax\\servlet\\javax.servlet-api\\3.1.0\\javax.servlet-api-3.1.0.jar,D:\\maven_resposity\\org\\apache\\commons\\commons-collections4\\4.1\\commons-collections4-4.1.jar,D:\\maven_resposity\\com\\ymm\\trade-boot-common\\1.1.5\\trade-boot-common-1.1.5.jar,D:\\maven_resposity\\com\\ymm\\lib\\lib_client_info_tools\\1.2.1\\lib_client_info_tools-1.2.1.jar,D:\\maven_resposity\\com\\ymm\\app\\plugin-info-tools\\1.0.1\\plugin-info-tools-1.0.1.jar,D:\\maven_resposity\\org\\jetbrains\\annotations\\15.0\\annotations-15.0.jar,D:\\maven_resposity\\org\\mapstruct\\mapstruct\\1.3.1.Final\\mapstruct-1.3.1.Final.jar,D:\\maven_resposity\\com\\alibaba\\fastjson\\1.2.70\\fastjson-1.2.70.jar,D:\\maven_resposity\\com\\ymm\\common-plugins\\common-beans\\1.0.5\\common-beans-1.0.5.jar,D:\\maven_resposity\\org\\projectlombok\\lombok\\1.18.4\\lombok-1.18.4.jar,D:\\maven_resposity\\com\\ymm\\security\\thaad-model\\1.1.3\\thaad-model-1.1.3.jar,D:\\maven_resposity\\net\\sf\\oval\\oval\\1.90\\oval-1.90.jar,D:\\maven_resposity\\org\\springframework\\boot\\spring-boot\\2.0.4.RELEASE\\spring-boot-2.0.4.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\spring-core\\5.0.8.RELEASE\\spring-core-5.0.8.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\spring-jcl\\5.0.8.RELEASE\\spring-jcl-5.0.8.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\spring-context\\5.0.8.RELEASE\\spring-context-5.0.8.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\spring-aop\\5.0.8.RELEASE\\spring-aop-5.0.8.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\spring-beans\\5.0.8.RELEASE\\spring-beans-5.0.8.RELEASE.jar,D:\\maven_resposity\\org\\springframework\\spring-expression\\5.0.8.RELEASE\\spring-expression-5.0.8.RELEASE.jar";
        String[] jararr = jarStr.split(",");

        Doclet doclet = new Doclet(
                "D:\\workspace\\RegionalOperation\\scheduler-sac-app\\scheduler-sac-api\\src\\main\\java\\com\\ymm\\scheduler\\sac\\facade\\cargo\\CargoQueryFacade.java"
                , Arrays.asList(jararr), Doclet.class.getClassLoader());
        ClassDoc classDoc = doclet.exec();
        log.info("类注释：" + classDoc.getModelCommentText());
        log.info("属性字段注释如下：");
        classDoc.getFieldEntryList().forEach(System.out::println);
        log.info("方法注释如下：");
        classDoc.getFieldEntryList().forEach(System.out::println);
    }

//    private Log getLog() {
//        if (this.log == null) {
//            this.log = new SystemStreamLog();
//        }
//
//        return this.log;
//    }
}
